package frontend.pages.reports.charismasynch;

import backend.model.admin.Employee;
import backend.model.admin.Team;
import backend.model.other.User;
import backend.model.other.MonthState;
import backend.model.worklog.WorkLogMismatch;
import backend.service.admin.EmployeeService;
import backend.service.other.MonthStateService;
import backend.service.other.UserService;
import backend.service.worklog.WorkLogMismatchService;
import backend.service.charisma.CharismaDataSynchronizer;
import frontend.components.other.EmployeeSelectionPanel;
import frontend.components.other.TeamSelectionPanel;
import frontend.core.TimeTrackingWebApplication;
import frontend.core.TimeTrackingWebSession;
import frontend.model.reports.ReportInputUIModel;
import frontend.pages.reports.base.AbstractReportForm;
import frontend.components.other.UserInputCallableAction;
import org.apache.wicket.Localizer;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.form.AjaxButton;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.util.time.Duration;
import org.apache.wicket.validation.ValidationError;
import shared.other.MonthStateEnum;
import shared.other.UserRolesDefinition;

import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class CharismaSynchronizationForm extends AbstractReportForm<WorkLogMismatch> {
    private static final String RESOURCE_KEY_VALIDATION_CLOSED_OR_BLOCKED_MONTH = "validation.form.charismaSynchronizationForm.closedOrBlockedMonth";
    public static final String PAGES_CHARISMA_SYNCHRONIZATION_REPORT_FORM_EMPLOYEE = "pages.charismaReport.form.employee";
    public static final String PAGES_CHARISMA_SYNCHRONIZATION_REPORT_FORM_TEAM = "pages.charismaReport.form.team";
    public static final String PAGES_CHARISMA_SYNCHRONIZATION_REPORT_VALIDATION_STARTED = "pages.charismaReport.form.validation.start";
    public static final String PAGES_CHARISMA_SYNCHRONIZATION_REPORT_VALIDATION_FAILED = "pages.charismaReport.form.validation.failed";
    public static final String PAGES_CHARISMA_SYNCHRONIZATION_REPORT_VALIDATION_SUCCESS = "pages.charismaReport.form.validation.success";

    private Panel employeeSelectionPanel;
    private Panel teamSelectionPanel;
    private AjaxButton runValidationButton;

    public CharismaSynchronizationForm(String id, ReportInputUIModel model, List<WorkLogMismatch> reportResults,
                                       WebMarkupContainer workLogListContainer) {
        super(id, model, reportResults, workLogListContainer);
    }

    @Override
    protected void addAdditionalFilteringComponents() {
        employeeSelectionPanel = new EmployeeSelectionPanel("employeeSelection", uiModel, getOnEmployeeSelectAction(), PAGES_CHARISMA_SYNCHRONIZATION_REPORT_FORM_EMPLOYEE, true);
        teamSelectionPanel = new TeamSelectionPanel("teamSelection", uiModel, getOnTeamSelectAction(), PAGES_CHARISMA_SYNCHRONIZATION_REPORT_FORM_TEAM, true);
        employeeSelectionPanel.setOutputMarkupId(true);
        teamSelectionPanel.setOutputMarkupId(true);
        runValidationButton = createRunValidationButton();
        add(employeeSelectionPanel, teamSelectionPanel, runValidationButton);
    }

    public UserInputCallableAction<Employee> getOnEmployeeSelectAction() {
        return new UserInputCallableAction<Employee>() {
            @Override
            public void call(AjaxRequestTarget target, Employee employee) {
                teamSelectionPanel.setEnabled(employee == null);
                target.add(teamSelectionPanel);
            }
        };
    }

    public UserInputCallableAction<Team> getOnTeamSelectAction() {
        return new UserInputCallableAction<Team>() {
            @Override
            public void call(AjaxRequestTarget target, Team team) {
                employeeSelectionPanel.setEnabled(team == null);
                target.add(employeeSelectionPanel);
            }
        };
    }

    private AjaxButton createRunValidationButton() {
        AjaxButton runValidationButton = new AjaxButton("runValidation", this) {
            @Override
            protected void onSubmit(final AjaxRequestTarget target, Form<?> form) {
                validationErrors.clear();
                doFormValidation();
                validateMonthIsNotBlockedOrClosed();

                if (validationErrors.size() > 0) {
                    for (ValidationError validationError : validationErrors) {
                        error(validationError.getMessage());
                    }
                } else {
                    runValidation(target);
                    this.setEnabled(false);
                    showReportResultsButton.setEnabled(false);
                    feedbackPanel.info(Localizer.get().getString(PAGES_CHARISMA_SYNCHRONIZATION_REPORT_VALIDATION_STARTED, null));
                }
                target.add(CharismaSynchronizationForm.this);
                hideResultsContainers(target);
            }

            @Override
            protected void onError(AjaxRequestTarget target, Form<?> form) {
                feedbackPanel.error(Localizer.get().getString(PAGES_CHARISMA_SYNCHRONIZATION_REPORT_VALIDATION_FAILED, null));
                setEnabled(true);
                target.add(CharismaSynchronizationForm.this);
            }
        };
        return runValidationButton;
    }

    private void runValidation(final AjaxRequestTarget target) {
        final ExecutorService executor = Executors.newFixedThreadPool(1);
        final CharismaDataSynchronizer charismaDataSynchronizer = TimeTrackingWebApplication.getBean(CharismaDataSynchronizer.class);
        Future futureTask = executor.submit(new Callable() {
            @Override
            public Object call() throws Exception {

                if (uiModel.getEmployee() != null) {
                    charismaDataSynchronizer.validateHoursForEmployeeAndTimeInterval(uiModel.getEmployee(), uiModel.getStartDate(), uiModel.getEndDate());
                } else if (uiModel.getTeam() != null) {
                    EmployeeService employeeService = TimeTrackingWebApplication.getService(EmployeeService.class);
                    List<Employee> employees = employeeService.findAllActiveEmployeesByTeam(uiModel.getTeam().getId());
                    charismaDataSynchronizer.validateHoursForEmployeeListAndTimeInterval(employees, uiModel.getStartDate(), uiModel.getEndDate());
                } else {
                    charismaDataSynchronizer.validateHoursForAllEmployeesAndTimeInterval(uiModel.getStartDate(), uiModel.getEndDate());
                }

                return null;
            }
        });
        FutureUpdateBehavior<Void> futureUpdateBehavior = new FutureUpdateBehavior<Void>(Duration.seconds(5), futureTask) {
            @Override
            protected void onPostSuccess(AjaxRequestTarget target) {
                feedbackPanel.info(Localizer.get().getString(PAGES_CHARISMA_SYNCHRONIZATION_REPORT_VALIDATION_SUCCESS, null));
                runValidationButton.setEnabled(true);
                showReportResultsButton.setEnabled(true);
                target.add(CharismaSynchronizationForm.this);
            }

            @Override
            protected void onUpdateError(AjaxRequestTarget target, Exception e) {
                feedbackPanel.error(Localizer.get().getString(PAGES_CHARISMA_SYNCHRONIZATION_REPORT_VALIDATION_FAILED, null));
                runValidationButton.setEnabled(true);
                showReportResultsButton.setEnabled(true);
                target.add(CharismaSynchronizationForm.this);
            }
        };

        add(futureUpdateBehavior);
        target.add(this);
    }

    @Override
    protected List<WorkLogMismatch> retrieveReportDataFromDatabase() {
        WorkLogMismatchService workLogMismatchService = TimeTrackingWebApplication.getService(WorkLogMismatchService.class);
        List<WorkLogMismatch> workLogMismatchListFromDB = null;

        if (uiModel.getEmployee() != null) {
            workLogMismatchListFromDB = workLogMismatchService.loadWorkLogMismatchForTimeIntervalAndEmployee(
                    uiModel.getEmployee(), uiModel.getStartDate(), uiModel.getEndDate());
        } else if (uiModel.getTeam() != null) {
            workLogMismatchListFromDB = workLogMismatchService.loadWorkLogMismatchForTimeIntervalAndTeam(
                    uiModel.getTeam(), uiModel.getStartDate(), uiModel.getEndDate());
        } else {
            workLogMismatchListFromDB = workLogMismatchService.loadWorkLogMismatchForTimeInterval(
                    uiModel.getStartDate(), uiModel.getEndDate());
        }
        return workLogMismatchListFromDB;
    }

    private void validateMonthIsNotBlockedOrClosed() {
        MonthStateService monthStateService = TimeTrackingWebApplication.getService(MonthStateService.class);
        String loggedInUsername = (String) TimeTrackingWebSession.get().getAttribute(TimeTrackingWebSession.LOGIN_USERNAME_ATTR);
        User loggedInUser = TimeTrackingWebApplication.getService(UserService.class).findUserByUsername(loggedInUsername);
        UserRolesDefinition.UserRole userRole = loggedInUser.getRole();

        Date startDate = uiModel.getStartDate();
        MonthState monthState = startDate != null ? monthStateService.findMonthStateByDate(startDate) : null;
        if (monthState != null) {
            if (monthState.getState() == MonthStateEnum.BLOCKED && !userRole.equals(UserRolesDefinition.UserRole.ADMIN)
                    || monthState.getState() == MonthStateEnum.CLOSED) {
                addFieldValidationError(RESOURCE_KEY_FIELD_LABEL_START_DATE, RESOURCE_KEY_VALIDATION_CLOSED_OR_BLOCKED_MONTH);
            }
        }

        Date endDate = uiModel.getEndDate();
        monthState = endDate != null ? monthStateService.findMonthStateByDate(endDate) : null;
        if (monthState != null) {
            if (monthState.getState() == MonthStateEnum.BLOCKED && !userRole.equals(UserRolesDefinition.UserRole.ADMIN)
                    || monthState.getState() == MonthStateEnum.CLOSED) {
                addFieldValidationError(RESOURCE_KEY_FIELD_LABEL_END_DATE, RESOURCE_KEY_VALIDATION_CLOSED_OR_BLOCKED_MONTH);
            }
        }

    }
}
